/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package com.perfma.xtest.tool;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.apache.jmeter.engine.util.CompoundVariable;
import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.Year;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.ThreadLocalRandom;
import java.util.regex.Pattern;

/**
 * RandomDate Function generates a date in a specific range
 * <p>
 * Parameters:
 * <ul>
 *  <li>Time format @see <a href="https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html">DateTimeFormatter</a>
 *      (optional - defaults to yyyy-MM-dd)</li>
 *  <li>Start date formated as first param (optional - defaults to now)</li>
 *  <li>End date</li>
 *  <li>Locale for the format (optional)</li>
 *  <li>variable name (optional)</li>
 * </ul>
 * Returns a formatted date with the specified number of (days, month, year)
 * Value is also saved in the variable for later re-use.
 *
 * @since 3.3
 */
public class RandomDate {

    private static final Logger log = LoggerFactory.getLogger(RandomDate.class);

    private static final String KEY = "__RandomDate"; // $NON-NLS-1$

    private static final int MIN_PARAMETER_COUNT = 3;

    private static final int MAX_PARAMETER_COUNT = 5;

    private static final List<String> desc = Arrays.asList(JMeterUtils.getResString("time_format_random"),
            JMeterUtils.getResString("date_start"), JMeterUtils.getResString("date_end"),
            JMeterUtils.getResString("locale_format"), JMeterUtils.getResString("function_name_paropt"));

    // Ensure that these are set, even if no parameters are provided
    private static Locale locale = new Locale("zh_CN"); // $NON-NLS-1$
    private static ZoneId systemDefaultZoneID = ZoneId.systemDefault(); // $NON-NLS-1$
    private CompoundVariable[] values;
    private static String[] TEL_FIRST = "134,135,136,137,138,139,147,150,151,152,157,158,159,165,172,178,182,183,184,187,188,195,130,131,132,145,155,156,166,167,171,175,176,185,186,133,149,153,162,173,174,177,180,181,189,191,199".split(",");


    private static final class LocaleFormatObject {

        private String format;
        private Locale locale;

        public LocaleFormatObject(String format, Locale locale) {
            this.format = format;
            this.locale = locale;
        }

        public String getFormat() {
            return format;
        }

        public Locale getLocale() {
            return locale;
        }

        @Override
        public int hashCode() {
            return format.hashCode() + locale.hashCode();
        }

        @Override
        public boolean equals(Object other) {
            if (!(other instanceof LocaleFormatObject)) {
                return false;
            }

            LocaleFormatObject otherError = (LocaleFormatObject) other;
            return format.equals(otherError.getFormat())
                    && locale.getDisplayName().equals(otherError.getLocale().getDisplayName());
        }

        /**
         * @see Object#toString()
         */
        @Override
        public String toString() {
            return "LocaleFormatObject [format=" + format + ", locale=" + locale + "]";
        }

    }

    /**
     * Date time format cache handler
     **/
    private static Cache<LocaleFormatObject, DateTimeFormatter> dateRandomFormatterCache = Caffeine.newBuilder().maximumSize(100).build();
    ;

    public RandomDate() {
        super();
    }

    /**
     * {@inheritDoc}
     */


//    public static void main(String[] args) {
//        for (int i = 0; i < 100; i++) {
//
//            DateTimeFormatter formatter;
//
//            String format = "yyyy-MM-dd HH:mm:ss.SSS";
//            if (!StringUtils.isEmpty(format)) {
//                try {
//                    LocaleFormatObject lfo = new LocaleFormatObject(format, locale);
//                    formatter = dateRandomFormatterCache.get(lfo, key -> createFormatter((LocaleFormatObject) key));
//                } catch (IllegalArgumentException ex) {
//                    log.error(
//                            "Format date pattern '{}' is invalid (see https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html)",
//                            format, ex); // $NON-NLS-1$
//                    return;
//                }
//            } else {
//                try {
//                    LocaleFormatObject lfo = new LocaleFormatObject("yyyy-MM-dd", locale);
//                    formatter = dateRandomFormatterCache.get(lfo, key -> createFormatter((LocaleFormatObject) key));
//                } catch (IllegalArgumentException ex) {
//                    log.error(
//                            "Format date pattern '{}' is invalid (see https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html)",
//                            format, ex); // $NON-NLS-1$
//                    return;
//                }
//            }
//
//            String dateStart = "2017-02-03";
//            long localStartTimestamp = 0;
//            if (!dateStart.isEmpty()) {
//                try {
//                    localStartTimestamp = LocalDateTime.parse(dateStart, formatter).toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
//                } catch (DateTimeParseException | NumberFormatException ex) {
//                    log.error("Failed to parse Start Date '{}'", dateStart, ex); // $NON-NLS-1$
//                }
//            } else {
//                try {
//                    localStartTimestamp = LocalDateTime.now(systemDefaultZoneID).toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
//                } catch (DateTimeParseException | NumberFormatException ex) {
//                    log.error("Failed to create current date '{}'", dateStart, ex); // $NON-NLS-1$
//                }
//            }
//            long localEndTimestamp = 0;
//            String dateEnd = "2023-05-05 00:00:00.321";
//            try {
//                localEndTimestamp = LocalDateTime.parse(dateEnd, formatter).toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
//            } catch (DateTimeParseException | NumberFormatException ex) {
//                log.error("Failed to parse End date '{}'", dateEnd, ex); // $NON-NLS-1$
//            }
//
//            // Generate the random date
//            String dateString = "";
//            if (localEndTimestamp < localStartTimestamp) {
//                log.error("End Date '{}' must be greater than Start Date '{}'", dateEnd, dateStart); // $NON-NLS-1$
//            } else {
//                long randomDay = ThreadLocalRandom.current().nextLong(localStartTimestamp, localEndTimestamp);
//                try {
//                    dateString = Instant.ofEpochMilli(randomDay).atZone(ZoneOffset.ofHours(8)).toLocalDateTime().format(formatter);
//                } catch (DateTimeParseException | NumberFormatException ex) {
//                    log.error("Failed to generate random date '{}'", randomDay, ex); // $NON-NLS-1$
//                }
//            }
//            System.out.println(dateString);
//        }
//    }
    public static void main(String[] args) {
//        for (int i = 0; i < 1000; i++) {
//            int index = getNum(TEL_FIRST.length);
//            String first = TEL_FIRST[index];
//            StringBuffer sb = new StringBuffer(first);
//            for (int j = 0; j < 8; j++) {
//                sb.append(getNum(9));
//            }
//            System.out.println(sb.toString());
//            String suffix = "@qa.cn";
//            if (!suffix.isEmpty()) {
//                suffix = suffix.startsWith("@") ? suffix : "@" + suffix;
//            }
//            System.out.println(getEmail(6, 18, suffix));
//        }

        String fmt = "/1000";
        Pattern TIME_DIVISOR_PATTERN = java.util.regex.Pattern.compile("/\\d+");
        if (TIME_DIVISOR_PATTERN.matcher(fmt).matches()) { // divisor is a positive number
            long div = Long.parseLong(fmt.substring(1)); // should never case NFE
            System.out.println(Long.toString(System.currentTimeMillis() / div));
        }
    }

    private static String BASE = "abcdefghijklmnopqrstuvwxyz0123456789";
    private static final String[] EMAIL_SUFFIX = "@gmail.com,@yahoo.com,@msn.com,@hotmail.com,@aol.com,@ask.com,@live.com,@qq.com,@0355.net,@163.com,@163.net,@263.net,@3721.net,@yeah.net,@googlemail.com,@126.com,@sina.com,@sohu.com,@yahoo.com.cn".split(",");

    private static String getEmail(int lMin, int lMax, String suffix) {
        int length = getNum(lMin, lMax);
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = (int) (Math.random() * BASE.length());
            sb.append(BASE.charAt(number));
        }
        if (suffix.isEmpty()) {
            suffix = EMAIL_SUFFIX[(int) (Math.random() * EMAIL_SUFFIX.length)];
        }
        sb.append(suffix);
        return sb.toString();
    }


    private static int getNum(int start, int end) {
        return ThreadLocalRandom.current().nextInt(start, end);
    }
//    private static int getNum(int end) {
//        return ThreadLocalRandom.current().nextInt(end);
//    }

    private static DateTimeFormatter createFormatter(LocaleFormatObject format) {
        log.debug("Create a new instance of DateTimeFormatter for format '{}' in the cache", format);
        return new DateTimeFormatterBuilder().appendPattern(format.getFormat())
                .parseDefaulting(ChronoField.MILLI_OF_SECOND, 0).parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
                .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0).parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
                .parseDefaulting(ChronoField.DAY_OF_MONTH, 1).parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
                .parseDefaulting(ChronoField.YEAR_OF_ERA, Year.now().getValue())
                .toFormatter(format.getLocale());

    }
}
